#!/usr/bin/env python
# -*- encoding:utf-8 -*-
#
# Copyright 2011 Harald Schilly <harald@gtug.at>
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
# http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

### DOCUMENTATION
#
# Input
# this program takes a csv list (download/export from google docs) where column 0 is 
# the name and column 1 is the email adress. it also discards the header line.

# Output
# Creates a directory qr, fills it with QR codes downloaded from the Google Visualizaion API.
# Outputs a SVG file called SVGFN with proper formatting for printing (default A4)
# you can either print the SVG directly (e.g. inkscape) or use inkscape to render a beautiful PDF (default).

import sys
from PyQRNative import *

# input filename (csv)
# example content:
# name,email,... [remaining fields don't matter]
# "firstname1 name1","email@email.com",...
# ...
if len(sys.argv) <= 1:
  print "USAGE: %s filename.csv [type = p|o, default p]" % sys.argv[0]
  print "       %s empty    ... to produce a sheet without names" % sys.argv[0]
  sys.exit(1)

CSVFN = None
BASEFN = "empty"
type = "p"
if sys.argv[1] != "empty":
  CSVFN = sys.argv[1]
  # output filename common prefix
  BASEFN = CSVFN.replace(".csv", "")

# settings file as optional second argument
SETTINGSFN = "settings.ini"

#type
if len(sys.argv) >= 3:
  type = sys.argv[2]
 
### READING SETTINGS
from ConfigParser import ConfigParser
C = ConfigParser()
C.read(SETTINGSFN)

BP = "res/bp.png"
BO = "res/bo.png"

pdims = [ C.getfloat("page", _) for _ in ["pdimheight", "pdimwidth"] ]
pmargin = [ C.getfloat("page", "pmargin%s"%_) for _ in ["top", "right", "bottom", "left" ]]
rows = C.getint("page", "rows")
cols = C.getint("page", "cols")
bspace = C.getfloat("page", "bspaceheight"), C.getfloat("page", "bspacewidth")
unit = C.get("page", "unit")

name_idx = 0
city_idx = 1
job_idx = 2
email_idx = 3
try:
  import shutil
  shutil.rmtree('temp/')
  print "temp folder cleared"
except OSError, err:
  pass

### actual Code starts here

import csv, urllib2, os, sys
from hashlib import md5
from collections import namedtuple

# QR code images end up in qr. delete the whole dir to refresh them.
if not os.path.exists("result"): os.mkdir("result")
if not os.path.exists("temp"): os.mkdir("temp")

# delete existing files
from glob import glob
for pat in ["result/%s-*.pdf", "temp/%s-*.svg"]:
  for f in glob(pat % BASEFN):
    os.remove(f)

# SVG specific functions
from svg import SVG


# calculating badge size info (height and width)
bdims = float(pdims[0] - pmargin[0] - pmargin[2] - bspace[0] * (rows-1)) / rows,\
        float(pdims[1] - pmargin[3] - pmargin[1] - bspace[1] * (cols-1)) / cols 

def draw_badge(svg, cnt, g):
  """
  here is the actual layout defined
  """
  #log
  print "start %s" %(g.name)
  
  # calc position on page, (x,y)
  offsetcnt = cnt % cols, (cnt % (rows*cols)) / cols 
  offset = pmargin[3] + offsetcnt[0] * (bdims[1] + bspace[1]),\
           pmargin[0] + offsetcnt[1] * (bdims[0] + bspace[0])
  
  # Background
  bg = BP
  if type == 'o':
    bg = BO
  svg.image(offset[0], offset[1], bdims[1], bdims[0], bg)

  # name
  svg.text(offset[0] + 3, offset[1] + 10, g.name.decode("utf8"), size=6, anchor="start", weight="bold")
  # job
  if(len(g.job) > 32):
     svg.text(offset[0] + 3, offset[1] + 16, g.job.decode("utf8"), size=3.3, anchor="start")
  else:
     svg.text(offset[0] + 3, offset[1] + 16, g.job.decode("utf8"), size=4, anchor="start")

  if(g.name != ""):
      #qr
      qr = QRCode(6, QRErrorCorrectLevel.L)
      qrData = "MECARD:N:%s;EMAIL:%s;NOTE:%s;;" %(g.name, g.email, g.job + ', ' + g.city)
      qr.addData(qrData)
      qr.make()
      qrImage = qr.makeImage()
      qrImagePath = "temp/qr-"+g.name+".png"
      qrImage.save(qrImagePath)
      svg.image(offset[0] + 2, offset[1] + 20, 20, 20, qrImagePath)
  
def draw_cutmarks(svg):
  # top and bottom
  for c in range(cols + 1):
    x = pmargin[3] + c * (bdims[1] + bspace[1]) - bspace[1] / 2.0 - 1
    svg.line((x, 0),                                     (x, pmargin[0] - bspace[0]))
    svg.line((x, pdims[0] - pmargin[2] + bspace[1] + 2), (x, pdims[0]))
  for r in range(rows + 1):
    y = pmargin[0] + r * (bdims[0] + bspace[0]) - bspace[0] / 2.0 + 1
    svg.line((0, y),                                     (pmargin[3] - bspace[1] - 2, y))
    svg.line((pdims[1] - pmargin[1] + bspace[0] , y), (pdims[1], y))

def draw_info(svg, n):
  from datetime import datetime
  date = datetime.strftime(datetime.utcnow(), "%Y-%m-%d %H:%M:%S UTC")
  fn = CSVFN if CSVFN != None else "empty sheet"
  svg.text(pmargin[3] + 40, pmargin[0] - 1, "%s - %s - %s" % (fn, n, date), size=2)


# utility function
def save_svg(svg, n):
  """
  saves the n-th svg file from the string svg
  """
  # if you change SVGFN, you also have to change the delete pattern above
  SVGFN = "temp/%s-%02d.svg" % (BASEFN, n)
  draw_cutmarks(svg)
  draw_info(svg, n)
  svg = svg.finalize()
  with codecs.open(SVGFN, 'w', 'utf-8-sig') as svgfile:
    svgfile.write(svg)
    print "output svg written to", os.path.abspath(SVGFN)
  # now convert the n-th svg file to pdf using inkscape
  # if you change the filepattern, you also have to change it in other spots
  os.system("inkscape --export-pdf=temp/%s-%02d.pdf temp/%s-%02d.svg" % (BASEFN, n, BASEFN, n))
  print "converted svg %d to pdf" % n


# primitive datacontainer
Guest = namedtuple('Guest', ['name', 'city', 'job', 'email'])

class Empty(object):
  # fake data to produce empty ones if option empty is set
  def __init__(self, n):
    self.n = n
  def __iter__(self):
    self.i = 0
    while self.i < self.n:
      yield self
      self.i += 1
  def __getitem__(self,index):
    return ""

import codecs
try:
  if CSVFN != None:
    content = csv.reader(codecs.open(CSVFN, "rb"))
  else:
    content = Empty(rows * cols + 1).__iter__()

except IOError, msg:
  print "You need a CSV file '%s' (=CSVFN variable) in the current directory." % CSVFN
  print msg
  sys.exit(1)

content.next() # skip header

# initialized and used in the for loop
svg = None # svg file content
n   = 0    # n-th page

longest_name = ""
longest_page = 0

# iterate over each named tuple Guest generated for selected elements in CSV's line list
data = map(lambda _ : Guest._make([_[name_idx], _[city_idx], _[job_idx], _[email_idx]]), content)

# fill remainder of last page with empty tags
nb_lines = sum(1 for _ in open(CSVFN)) - 1
for i in range((rows * cols ) - nb_lines % (rows * cols)):
  data.append(Guest._make(["", "", "", ""]))

#for i in range((rows * cols )*2):
#  data.append(Guest._make(["", "", "", ""]))
# iterate over sorted list by surname. names like "  name   surname  " are fine.
for cnt, g in enumerate(sorted(data, key = lambda _:_.name.lower().strip().split(" ")[-1])):


  #print g.name, urllib2.quote(g.email), url
  if len(g.name) > len(longest_name):
    longest_name = g.name
    longest_page = n

  # check if we start a new page
  if cnt % (rows * cols) == 0:
    if svg != None:
       # save the file
       save_svg(svg, n)
       n += 1
    svg = SVG(pdims, unit)

  draw_badge(svg, cnt, g)


# close last page
save_svg(svg, n)

print "Check longest name '%s'" % longest_name, "'on page", longest_page, "for clipping."

# this is for after everthing is done. it combines all PDFs into one using pdftk.
# if you change the filepattern, you also have to change it in other spots
os.system("pdftk temp/%s-*.pdf output result/%s.pdf" % (BASEFN, BASEFN))
# this one is for convert, not tested
#os.system("convert %s-*.pdf %s.pdf" % (BASEFN, BASEFN))
print "Collected all PDFs into %s.pdf" % BASEFN

